﻿#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os
import zipfile

def is_archive(path):
    abs_path = os.path.abspath(path)

    return zipfile.is_zipfile(abs_path)

class ZipError(Exception):
    def __init__(self, message):
        Exception.__init__(self, message)        
    

class ZipArchiver:
    def archive(self, source_dir, out_file):
        zip = None
        try:
            zip = zipfile.ZipFile(out_file, 'w', zipfile.ZIP_DEFLATED)
        
            for root, dirs, files in os.walk(source_dir):
                for dir in dirs:
                    if dir.startswith("."):
                        dirs.remove(dir)
                        
                for file in files:
                    if not file.startswith("."):
                        abs_path = os.path.abspath(os.path.join(root, file))
                        virtualname = self.__relpath(abs_path, os.path.abspath(source_dir) + os.sep)
    
                    zip.write(abs_path, virtualname)
                if not files and not dirs:
                    abs_path = os.path.abspath(root)
                    virtualname = self.__relpath(abs_path, os.path.abspath(source_dir) + os.sep)
                    zip_file_info = zipfile.ZipInfo(virtualname)
                    zip_file_info.external_attr = 16
                    zip.writestr(zip_file_info, "")
        except Exception, error:            
            raise ZipError(error.message)
        finally:
            if zip: zip.close()

    def extract(self, zip_file, out_path):
        zip = None
        try:
            zip = zipfile.ZipFile(zip_file, 'r', zipfile.ZIP_DEFLATED)
            
            for info in zip.namelist():
                destination_file_path = os.path.join(out_path, info)
                destination_dir_path = "/".join(destination_file_path.split("/")[:-1])
                if destination_dir_path and not os.path.exists(destination_dir_path):
                    os.makedirs(os.path.abspath(destination_dir_path))
                
                if not os.path.isdir(destination_file_path):
                    file = open(destination_file_path, 'wb')
                    try:
                        file.write(zip.read(info))
                        file.flush()
                    finally:
                        file.close()
                        
        except Exception, error:
            raise ZipError(error.message)
        
        finally:
            if zip: zip.close()
        
    def __relpath(self, path, reldir):
        #http://code.activestate.com/recipes/302594/
        #returns 'path' relative to 'reldir'."""
    
        # use normpath to ensure path separators are uniform
        path = os.path.normpath(path)
    
        # find length of reldir as prefix of path (or zero if it isn't)
        prelen = len(os.path.commonprefix((
            os.path.normcase(path),
            # add a separator to get correct prefix length
            # (normpath removes trailing separators)
            os.path.normcase(os.path.normpath(reldir)) + os.sep
            )))
        return path[prelen:]
